/******************************************************************************
 *
 * Copyright:
 *    (C) 2005 Embedded Artists AB
 *
 * File:
 *    uart.c
 *
 * Description:
 *    Implementation of interrupt driven UART.
 *
 *****************************************************************************/

/******************************************************************************
 * Includes
 *****************************************************************************/
#include "general.h"
#include <lpc2xxx.h>
#include "uart.h"
#include "startup/irq_uart.h"

extern uart_t *uart0ptr;
extern uart_t *uart1ptr;

/*****************************************************************************
 * Implementation of public functions
 ****************************************************************************/

/*****************************************************************************
 *
 * Description:
 *    Initialize UART #0 in polled mode, i.e., interrupts are not used.
 *
 * Parameters:
 *    [in] div_factor - UART clock division factor to get desired bit rate.
 *                      Use definitions in uart.h to calculate correct value.
 *    [in] mode       - transmission format settings. Use constants in uart.h
 *    [in] fifo_mode  - FIFO control settings. Use constants in uart.h
 *
 ****************************************************************************/
void initUart(uart_t *uart, tU16 div_factor, tU8 mode, tU8 fifo_mode)
{
    volatile tU32 dummy;

    // enable uart pins in GPIO: 
    //   UART0(P0.0 = TxD0, P0.1 = RxD0)
    //   UART1(P0.8 = TxD1, P0.9 = RxD1)
    if (uart->uartRegBase == UART0_REG_BASE)
        PINSEL0 = (PINSEL0 & 0xfffffff0) | 0x00000005;
    else
        PINSEL0 = (PINSEL0 & 0xfff0ffff) | 0x00050000;

    REG(uart->uartRegBase + UIER) = 0x00;   //disable all uart interrupts
    dummy = REG(uart->uartRegBase + UIIR);  //clear all pending interrupts
    dummy = REG(uart->uartRegBase + URBR);  //clear receive register
    dummy = REG(uart->uartRegBase + ULSR);  //clear line status register

    //set the bit rate = set uart clock (pclk) divisionfactor
    REG(uart->uartRegBase + ULCR) = 0x80;                   //enable divisor latches (DLAB bit set, bit 7)
    REG(uart->uartRegBase + UDLL) = (tU8)div_factor;        //write division factor LSB
    REG(uart->uartRegBase + UDLM) = (tU8)(div_factor >> 8); //write division factor MSB

    //set transmissiion and fifo mode
    REG(uart->uartRegBase + ULCR) = (mode & ~0x80);         //DLAB bit (bit 7) must be reset
    REG(uart->uartRegBase + UFCR) = fifo_mode;

    uartPurge(uart);

    //initialize the interrupt vector
    if (uart->uartRegBase == UART0_REG_BASE) {
        uart0ptr = uart;
        VICIntSelect &= ~0x00000040;        // UART0 selected as IRQ
        VICVectCntl6  =  0x00000026;        // enable vectored ISR slot 6 and give it unique number
        VICVectAddr6  =  (tU32)uart0ISR;    // address of the ISR
        VICIntEnable |=  0x00000040;        // UART0 interrupt enabled
    }
    else {
        uart1ptr = uart;
        VICIntSelect &= ~0x00000080;      // UART1 selected as IRQ
        VICVectCntl7  =  0x00000027;      // enable vectored ISR slot 7 and give it unique number
        VICVectAddr7  =  (tU32)uart1ISR;  // address of the ISR
        VICIntEnable |=  0x00000080;      // UART1 interrupt enabled
    }

    //enable receiver interrupts
    REG(uart->uartRegBase + UIER) = 0x01;
}

/*****************************************************************************
 *
 * Description:
 *    Blocking output routine, i.e., the routine waits until the uart 
 *    buffer is free and then sends the character. 
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *    [in] charToSend - The character to print (to uart #0) 
 *
 ****************************************************************************/
void uartSendChar(uart_t *u, tU8 charToSend)
{
    volatile tU32 cpsrReg;
    tU32 tmpHead;

    //calculate head index
    tmpHead = (u->uartTxHead + 1) & TX_BUFFER_MASK; 

    //wait for free space in buffer
    while (tmpHead == u->uartTxTail) 
        ;

    //disable IRQ
    cpsrReg = disIrq();

    REG(u->uartRegBase + UIER) &= ~0x02;   //disable TX IRQ

    //enable IRQ
    restoreIrq(cpsrReg);

    if (u->uartTxRunning == TRUE) {
        u->uartTxBuf[tmpHead] = charToSend; 
        u->uartTxHead = tmpHead;
    }
    else {
        u->uartTxRunning = TRUE;
        REG(u->uartRegBase + UTHR) = charToSend;
    }

    //disable IRQ
    cpsrReg = disIrq();

    REG(u->uartRegBase + UIER) |= 0x02;   //enable TX IRQ

    //enable IRQ
    restoreIrq(cpsrReg);
}


/*****************************************************************************
 *
 * Description:
 *    Output routine that adds extra line feeds at line breaks. 
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *    [in] charToSend - The character to print (to uart #0) 
 *
 ****************************************************************************/
void uartSendCh(uart_t *u, tU8 charToSend)
{
    if (charToSend == '\n')
        uartSendChar(u, '\r');

    uartSendChar(u, charToSend);
}


/*****************************************************************************
 *
 * Description:
 *    Print NULL-terminated string to uart #0. 
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *    [in] pString - Pointer to NULL-terminated string to be printed 
 *
 ****************************************************************************/
void uartSendString(uart_t *u, tU8 *pString)
{
    while (*pString)
        uartSendCh(u, *pString++);
}

/*****************************************************************************
 *
 * Description:
 *    Print a fixed number of bytes (as opposed to NULL-terminated string).
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *    [in] pBuff - The character to print (to uart #0) 
 *    [in] count - Number of characters to print
 *
 ****************************************************************************/
void uartSendChars(uart_t *u, char *pBuff, tU16 count)
{
    while (count--)
        uartSendChar(u, *pBuff++);
}

/*****************************************************************************
 *
 * Description:
 *    Blocking function that waits for a received character. 
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 * Return:
 *    The received character. 
 *
 ****************************************************************************/
tU8 uartGetCh(uart_t *u)
{
    tU8 rxChar;

    //wait for a character to be available
    while (uartGetChar(u, &rxChar) == FALSE) 
        ;
    return rxChar;
}

/*****************************************************************************
 *
 * Description:
 *    Non-blocking receive function.
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *    [in] pRxChar - Pointer to buffer where the received character shall
 *                   be placed.
 * Return:
 *    TRUE if character was received, else FALSE.
 *
 ****************************************************************************/
tU8 uartGetChar(uart_t *u, tU8 *pRxChar)
{
    tU32 tmpTail;

    /* buffer is empty */
    if (u->uartRxHead == u->uartRxTail)
        return FALSE;

    tmpTail = (u->uartRxTail + 1) & RX_BUFFER_MASK;
    u->uartRxTail = tmpTail; 

    *pRxChar = u->uartRxBuf[tmpTail];
    return TRUE;
}

/*****************************************************************************
 *
 * Description:
 *    Check if there are any characters in the receive buffer.
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *
 * Return:
 *    TRUE if there characters available for reading.
 *
 ****************************************************************************/
tU8 uartCharAvail(uart_t *u)
{
    return (u->uartRxHead != u->uartRxTail);
}

/*****************************************************************************
 *
 * Description:
 *    Discards data in the Rx/Tx buffers.
 *
 * Params:
 *    [in] uart  - pointer to uart_t structure 
 *
 * Return:
 *      n/a
 *
 ****************************************************************************/
void uartPurge(uart_t *uart)
{
    //initialize the transmit data queue
    uart->uartTxHead    = 0;
    uart->uartTxTail    = 0;
    uart->uartTxRunning = FALSE;

    //initialize the receive data queue
    uart->uartRxHead   = 0;
    uart->uartRxTail   = 0;
}

